/*
 * @(#)LuceneRawStorer.java	0.1 01/07/05
 *
 * Copyright 2004 Fabio Gasparetti. All rights reserved.
 */

package org.tabularium.text.se;


import java.util.logging.Logger;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Iterator;

import org.apache.commons.io.FileUtils;
import org.apache.lucene.document.*;
import org.apache.lucene.analysis.*;
import org.apache.lucene.index.*;
import org.apache.lucene.search.*;
import org.apache.lucene.queryParser.*;
import org.tabularium.net.downloader.DefaultResourceFactory;
import org.tabularium.net.downloader.Resource;
import org.tabularium.text.TermWeightVector;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;

import java.util.*;

/**
 * Note: if you use the same index for indexing/searching and raw storing pay
 * attention to concurrent access issues. The method HitsIterator.getId()
 * returns urls with escaped quotes.
 * 
 * @todo: create a resource class to store and retrieve multiple fields.
 */
public class LuceneRawStorer extends RawStorer {
	protected String dir;
	protected Analyzer analyzer;
	protected IndexWriter writer = null;
	protected PerFieldAnalyzerWrapper fieldAnalyzer;
	protected Logger logger = Logger
			.getLogger("org.tabularium.text.se.LuceneRawStorer");

	public LuceneRawStorer(String dir) throws IOException {
		this.dir = dir;
		// creates analyzer instances
		this.analyzer = new SimpleAnalyzer();
		fieldAnalyzer = new PerFieldAnalyzerWrapper(analyzer);
		fieldAnalyzer.addAnalyzer("idstorer", new UrlFieldAnalyzer());
		fieldAnalyzer.addAnalyzer("timestamp", new UrlFieldAnalyzer());

		if (!IndexReader.indexExists(dir)) {
			clear();
		}
	}

	/**
	 * Remember to call LuceneSearchEngine.escape() if the id contains special characters.
	 */
	public void store(String id, byte[] obj) throws IOException {
		store(id, obj, null, System.currentTimeMillis());
	}

	protected void store(String id, byte[] obj, String res, long timestamp) throws IOException {
		Document lucenedoc = new Document();
		// not tokenized but indexed
		if (id != null) {
			lucenedoc.add(new org.apache.lucene.document.Field("idstorer", id, true, true,
					false));
		}
		// only stored, not indexed nor tokenized
		if (obj != null) {
			lucenedoc
					.add(new org.apache.lucene.document.Field("body", new String(obj), true, false, false));
		}
		if (res != null) {
			lucenedoc.add(new org.apache.lucene.document.Field("resource", res, true, false, false));
		}
		lucenedoc.add(new org.apache.lucene.document.Field("timestamp", Long.toString(timestamp), true, true, false));

		// no one must delete while new docs are inserted
		synchronized (this) {
			writer = new IndexWriter(dir, fieldAnalyzer, false);
			writer.addDocument(lucenedoc);
			writer.close();
		}
	}
	
	/**
	 * Remember to call LuceneSearchEngine.escape() if the Resource's url contains special characters.
	 */
	public void store(Resource res) throws IOException {
		String s = null;
		XStream xs = new XStream();
		s = xs.toXML(res);
		store(res.getURL(), res.getObject(), s, System.currentTimeMillis());
	}

	/**
	 * Remember to call LuceneSearchEngine.escape() if the id contains special characters.
	 */
	public List<byte[]> retrieve(String id) throws IOException {
		List<byte[]> ret = new ArrayList<byte[]>();
		String body = null;
		List<Document> docs = retrieveDocument(id);
		Iterator<Document> docIter = docs.iterator();
		while (docIter.hasNext()) {
			Document doc = (Document) docIter.next();
			body = doc.get("body");
			if (body != null)
				ret.add(body.getBytes());
			
		}
		return ret;
	}
	
	protected List<Document> retrieveDocument(String id) throws IOException {
		List<Document> docs = new ArrayList<Document>();
		Searcher searcher = new IndexSearcher(dir);

		String query = new String("+idstorer:\"" + id + "\"");

		Query q = null;
		try {
			q = QueryParser.parse(query, "idstorer", fieldAnalyzer);
		} catch (Exception ex) {
			throw new IOException("Query exception: " + ex.toString());
		}
		Hits hits = searcher.search(q);
		for (int i = 0; i < hits.length(); i++) {
			docs.add(hits.doc(i));
		}
		searcher.close();
		return docs;
	}

	protected Document retrieveDocument(String id, long timestamp) throws IOException {
		Document ret = null;
		Searcher searcher = new IndexSearcher(dir);

		String query = new String("+idstorer:\"" + id + "\" +timestamp:"+timestamp);

		Query q = null;
		try {
			q = QueryParser.parse(query, "idstorer", fieldAnalyzer);
		} catch (Exception ex) {
			throw new IOException("Query exception: " + ex.toString());
		}
		Hits hits = searcher.search(q);
		if (hits.length() > 0) {
			ret = hits.doc(0);
		}
		searcher.close();
		return ret;
	}

		
	/**
	 * Remember to call LuceneSearchEngine.escape() if the id contains special characters.
	 */
	public List<Resource> retrieveResource(String id) throws IOException {
		List<Resource> ress = new ArrayList<Resource>();
		String s = null;
		Resource res = null;
		List<Document> docs = retrieveDocument(id);
		Iterator<Document> docIter = docs.iterator();
		while (docIter.hasNext()) {
			Document document = (Document) docIter.next();
			s = document.get("resource");
			if (s != null) {
				XStream xs = new XStream(new DomDriver());
				res = (Resource)xs.fromXML(s);
				ress.add(res);
			}
		}		
		return ress;
	}

	/**
	 * Remember to call LuceneSearchEngine.escape() if the id contains special characters.
	 */
	public List<Long> timestamp(String id) throws IOException {
		List<Long> tss = new ArrayList<Long>();
		String ret = null;
		Searcher searcher = new IndexSearcher(dir);

		String query = new String("+idstorer:\"" + id + "\"");

		Query q = null;
		try {
			q = QueryParser.parse(query, "idstorer", fieldAnalyzer);
		} catch (Exception ex) {
			throw new IOException("Query exception: " + ex.toString());
		}
		Hits hits = searcher.search(q);
		for (int i = 0; i < hits.length(); i++) {
			Document doc = hits.doc(i);
			ret = doc.get("timestamp");
			if (ret != null) {
				tss.add(new Long(ret));
			}
		}
		searcher.close();
		return tss;
	}

	public DocIterator browse() throws IOException {
		IndexReader reader = IndexReader.open(dir);
		return new LuceneDocIterator(reader);
	}

	public void remove(String id) throws IOException {
		throw new IOException("Not yet defined");
		/** @todo to develop */
	}

	// delete the index
	public void clear() throws IOException {
		FileUtils.cleanDirectory(new File(dir));
		writer = new IndexWriter(dir, analyzer, true);
		writer.close();
	}

	public int size() throws IOException {
		IndexReader reader = IndexReader.open(dir);
		int n = reader.numDocs();
		reader.close();
		return n;
	}


	


	/**
	 * Remember to call LuceneSearchEngine.escape() if the id contains special characters.
	 */
	public void store(String id, byte[] obj, long timestamp) throws IOException {
		store(id, obj, null, timestamp);
	}

	/**
	 * Remember to call LuceneSearchEngine.escape() if the id contains special characters.
	 */

	public byte[] retrieve(String id, long timestamp) throws IOException {
		byte[] ret = null;
		String body = null;
		Document doc = retrieveDocument(id, timestamp);
		body = doc.get("body");
		if (body != null)
			ret = body.getBytes();			
		return ret;
	}

	/**
	 * Remember to call LuceneSearchEngine.escape() if the id contains special characters.
	 */
	public Resource retrieveResource(String id, long timestamp)
			throws IOException {
		String s = null;
		Resource res = null;
		Document doc = retrieveDocument(id, timestamp);
		if (doc != null) {
			s = doc.get("resource");
			if (s != null) {
				XStream xs = new XStream(new DomDriver());
				res = (Resource)xs.fromXML(s);
			}
		}		
		return res;
	}

	@Override
	public void remove(String id, long timestamp) throws IOException {
		// TODO Auto-generated method stub
		
	}
	
	public static void main(String[] args) {
		LuceneRawStorer rawPageStorer;
		try {
			rawPageStorer = new LuceneRawStorer("./index");
			rawPageStorer.clear();
			DefaultResourceFactory drf = new DefaultResourceFactory();
			rawPageStorer.store("1", (new String("text1a")).getBytes(), 100);
			rawPageStorer.store("1", (new String("text1b")).getBytes(), 101);
			rawPageStorer.store("2", (new String("text2c")).getBytes(), 102);
			List<byte[]> bb = rawPageStorer.retrieve("1");
			Iterator<byte[]> iterBb = bb.iterator();
			while (iterBb.hasNext()) {
				byte[] bs = (byte[]) iterBb.next();
				System.out.println(new String(bs));
			}
			System.out.println();
			bb = rawPageStorer.retrieve("2");
			iterBb = bb.iterator();
			while (iterBb.hasNext()) {
				byte[] bs = (byte[]) iterBb.next();
				System.out.println(new String(bs));
			}
			System.out.println();
			bb = rawPageStorer.retrieve("3");
			iterBb = bb.iterator();
			while (iterBb.hasNext()) {
				byte[] bs = (byte[]) iterBb.next();
				System.out.println(new String(bs));
			}
			System.out.println();
			System.out.println(new String(rawPageStorer.retrieve("1", 100)));
			System.out.println(new String(rawPageStorer.retrieve("1", 101)));
			//System.out.println(new String(rawPageStorer.retrieve("1", 103)));
			System.out.println(new String(rawPageStorer.retrieve("2", 102)));
			System.out.println(new String(rawPageStorer.retrieve("2", 101)));
		} catch (Exception ex) {
			System.err.println(ex.toString());
			return;
		}
	}

}